テストのことを考えると family Provider を作るよりも Function を返す Provider の方が書き味がいい
最初に結論としては
@riverpod
Weather getWeather(GetWeatherRef ref, {required String area}) { ... }
この代わりに
typedef GetWeatherUseCase = Weather Function({required String area});
@riverpod
GetWeatherUseCase getWeather(GetWeatherRef ref) {
return ({required area}) { ... }
}
こういう Provider を作るとテストの書き味が段違いという話。
Swift の protocol witness にちょっとイメージは近いかも。
背景・理由
以下が family Provider。引数を持つ Provider (ここだと area
) を作りたいときは family になる。
なぜ family と呼ぶかというのは Flutter Riverpodの.familyについて #Flutter - Qiita をさらっと読むといいかもしれない。
一意性を担保するというのがイメージ。
@riverpod
Weather getWeather(GetWeatherRef ref, {required String area}) { ... }
この Provider をテスト等で override しようとすると、 overrideWith
メソッドが出てこないはず。
createContainer(
overrides: [
getWeather.overrideWith // !!!! ← が出てこない
]
)
じゃあどうするかというと、引数を与える必要がある。
この動作は一見バグっぽく見えるが、最初に言った通り一意性を与える必要があると考えると自然な動作。
Generated family providers lack a `.overrideWith` method · Issue #3009 · rrousselGit/riverpod · GitHub に書いてあった気がする。
createContainer(
overrides: [
getWeather(area: 'tokyo').overrideWith(...)
]
)
だがしかし、これだと引数が固定されてしまって少し微妙である。
引数が変動する場合のテストが書けなくなってしまう。
ということで、 Result Function(ArgType)
で作るといいよ、という話になる。
2024-07-05 追記
サービスロケータのアンチパターンとされる問題も絡んできそう。DI コンテナであればこの辺りは解消される。